import os
import numpy as np
import sys

from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QThread, Qt, QRegExp
from PyQt5.QtWidgets import QMainWindow, QApplication, QInputDialog, QFileDialog, \
    QMessageBox, QColorDialog, QListWidgetItem, QTableWidgetItem, QProgressBar, \
    QRadioButton, QMdiSubWindow, QWidget, QLineEdit
from PyQt5.QtGui import QCloseEvent, QHideEvent, QRegExpValidator, QDoubleValidator

from PyQt5.QtCore import QTimer
from PyQt5.uic import loadUi

# mayavi 库
from traits.api import HasTraits, Instance, on_trait_change
from traitsui.api import View, Item
from mayavi.core.ui.api import MayaviScene, MlabSceneModel, \
    SceneEditor

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
import matplotlib.pyplot as plt

import time

class Form(QWidget):

    def __init__(self, iFlag_ui = 0):
        super(Form, self).__init__()

        self.iFlgsql_ui = iFlag_ui
        self.SqlTableModel = None
        if self.iFlgsql_ui:
            self.Init_UI()

    def Init_UI(self):

        loadUi('sql_hems.ui', self)
        self.open_pushButton.clicked.connect(self.open_db)
        self.open_pushButton.setToolTip('输入数据库名和表格名，打开或创建一个数据库')
        self.add_pushButton.clicked.connect(self.add_row_data)
        self.add_pushButton.setToolTip('通过界面输入一个数据并插入到当前数据库')
        self.delete_pushButton.clicked.connect(self.del_row_data)
        self.delete_pushButton.setToolTip('首先选择一行数据，然后点击进行删除')
        self.save_pushButton.clicked.connect(self.close_db)
        self.save_pushButton.setToolTip('保存数据，关闭当前的数据库链接')
        self.clear_pushButton.clicked.connect(self.clearFilter)
        self.clear_pushButton.setToolTip('清空过滤选项')
        self.ascend_radioButton.clicked.connect(self.sort_Data)
        self.descend_radioButton.clicked.connect(self.sort_Data)
        self.query_pushButton.clicked.connect(self.select_Data)
        self.pushButton_drawRcs.clicked.connect(self.draw_Rcs)

        self.radioButton_plot3D.clicked.connect(self.set_attr_button)
        self.radioButton_plot2D.clicked.connect(self.set_attr_button)

        reg = QRegExp("^\d+(\.\d+)?$")
        self.regExpValidator = QRegExpValidator()
        self.regExpValidator.setRegExp(reg)
        self.doubleValidator = QDoubleValidator()


    def set_attr_button(self):
        if self.radioButton_plot2D.isChecked():
            self.query_comboBox_plot_angle.setEnabled(True)
            self.lineEdit_plot_angle.setEnabled(True)

        if self.radioButton_plot3D.isChecked():
            self.query_comboBox_plot_angle.setEnabled(False)
            self.lineEdit_plot_angle.setEnabled(False)

    class Visualization(HasTraits):
        scene = Instance(MlabSceneModel, ())

        @on_trait_change('scene.activated')
        def update_plot(self):
            # This function is called when the view is opened. We don't
            # populate the scene when the view is not yet open, as some
            # VTK features require a GLContext.
            # We can do normal mlab calls on the embedded scene.

            # self.scene.mlab.pipeline.surface(self.scene.mlab.pipeline.open("cylinder.vtk"))
            # self.scene.mlab.test_points3d()
            pass

        # the layout of the dialog screated
        view = View(Item('scene', editor = SceneEditor(scene_class = MayaviScene),
                         height = 250, width = 300, show_label = False),
                         resizable = True  # We need this to resize with the parent widget
                    )

    def mayavi_draw(self):
        self.mayavi_visualization = self.Visualization()

        # 将mayavi窗口与PyQt5的界面进行绑定
        self.mayavi_ui = self.mayavi_visualization.edit_traits(parent=self, kind='subpanel').control
        self.layoutRcs.addWidget(self.mayavi_ui)
        self.mayavi_ui.setParent(self)

        def sph2cart(r, phi, theta):
            phi_rad     = np.array(phi)*np.pi/180
            theta_rad   = np.array(theta)*np.pi/180
            X = r * np.cos(phi_rad) * np.sin(theta_rad)
            Y = r * np.sin(phi_rad) * np.sin(theta_rad)
            Z = r * np.cos(theta_rad)
            return (X, Y, Z)

        # 方向图的强度表达式
        # F = 100 * abs(np.sin(theta) ** 3)
        # Fmin, Fmax = np.min(F), np.max(F)
        # Flen = Fmax - Fmin
        F = self.ListOfPointF
        Fmin, Fmax = np.min(self.ListOfPointF),  np.max(self.ListOfPointF)
        Flen = Fmax - Fmin

        # 画出几何模型
        if self.root.model.vertices is None:
            QMessageBox.information(self, "提示", "未找到几何模型文件，请先在模型模块中导入一个几何模型")
            return -1

        pointer1 = self.root.model.vertices
        fmeanX, fmeanY, fmeanZ = np.mean(pointer1[:, 0]), np.mean(pointer1[:, 1]), np.mean(pointer1[:, 2])
        fmaxX, fmaxY, fmaxZ = np.max(pointer1[:, 0]), np.max(pointer1[:, 1]), np.max(pointer1[:, 2])
        fminX, fminY, fminZ = np.min(pointer1[:, 0]), np.min(pointer1[:, 1]), np.min(pointer1[:, 2])
        flenX, flenY, flenZ = fmaxX-fminX, fmaxY-fminY, fmaxZ-fminZ
        flenMaxOfModel = np.max([flenX, flenY, flenZ])

        # 构造方向图合适的显示比例
        fscale   = 3.0*flenMaxOfModel/Flen

        pointer2 = self.root.model.facets
        self.mayavi_visualization.scene.mlab.triangular_mesh(pointer1[:, 0], pointer1[:, 1], pointer1[:, 2], pointer2,
                                                             colormap='copper')
        # 按照球坐标的形式将theta进行坐标转换,并将结果转化为笛卡尔坐标系
        # x, y, z = sph2cart(fscale*(F-Fmin) + 2*Fmin, phi, theta)
        x, y, z = sph2cart(fscale*(F-Fmin) +1.25*flenMaxOfModel, self.ListOfPointPhi, self.ListOfPointTheta)

        # 按照一定比例画出方向图
        # self.mayavi_visualization.scene.mlab.mesh(x + fmeanX, y + fmeanY, z + fmeanZ, opacity=0.5,
        #                                           color=(0.5, 0.5, 0.5))
        self.mayavi_visualization.scene.mlab.points3d(x + fmeanX, y + fmeanY, z + fmeanZ, (F-Fmin)/Flen,
                                                     scale_factor = 0.25, scale_mode="none", colormap='jet', opacity=0.15)

    #------画RCS曲线或者是RCS3D方向图
    def draw_Rcs(self):
        self.ListOfPointTheta = []
        self.ListOfPointPhi = []
        self.ListOfPointF   = []
        if self.radioButton_plot3D.isChecked():
            print("进入3DRCS方向图模块")
            if self.lineEdit_plot_freq.text() == '':
                QMessageBox.information(self, "提示", "请输入所要查询的频率！")
                return
            else:
                fFreqQuery = float(self.lineEdit_plot_freq.text())

            query_key = self.query_comboBox_polar.currentText()
            if query_key == 'rcs_vv':
               iQueryPolar = 1
            elif query_key == 'rcs_hh':
               iQueryPolar = 2
            else:
                QMessageBox.information(self, "提示", "请选择所要查询的极化方式！")
                return
            if not self.db_list:
                QMessageBox.information(self, "提示", "请打开一个工程或者打开一个数据库！")
                return
            query = QSqlQuery(self.db_list[-1])
            iFlag = query.exec(f"select * from {self.db_table_name}  WHERE freq={fFreqQuery}")
            if not iFlag:
                QMessageBox.warning(self, "提示", "未能正确读取数据中的数据！")
                return

            if iQueryPolar == 1:
                while (query.next()):       # 判断是否有下一条记录
                    self.ListOfPointTheta.append(90.0 - float(query.value(2))) #对theta角度进行转换，从俯仰角转换为标准球坐标系
                    self.ListOfPointPhi.append(float(query.value(3)))
                    self.ListOfPointF.append(float(query.value(4)))
            else:
                while (query.next()):  # 判断是否有下一条记录
                    self.ListOfPointTheta.append(90.0 - float(query.value(2)))
                    self.ListOfPointPhi.append( float(query.value(3)) )
                    self.ListOfPointF.append( float(query.value(5)) )
            self.mayavi_draw()

    #--------------清除数据库的过滤选项------------------
    def clearFilter(self):
        if self.SqlTableModel == None:
            QMessageBox.information(self, "提示", "请先打开一个数据库！")
        else:
            self.SqlTableModel.setTable(self.db_table_name)
            self.SqlTableModel.select()

    #--------------查询数据库对应表中的信息---------------
    def select_Data(self):
        # print("进入查询函数")
        if self.SqlTableModel == None:
            QMessageBox.information(self, "提示", "请先打开一个数据库！")
            return
        if self.query_comboBox.currentIndex() == 0:
            QMessageBox.information(self, "提示", "请选择查询字段！")
            return

        if self.query_lineEdit.text() == '':
            QMessageBox.information(self, "提示", "请输入所要查询的字段值！")
            return

        query_key = self.query_comboBox.currentText()
        List_temp = ['freq', 'elev', 'azim', 'rcs_vv', 'rcs_hh']
        if query_key not in List_temp:
            QMessageBox.Warning(self, "警告", "数据库中未查询到对应字段！")
            return
        select_value = float(self.query_lineEdit.text())

        self.SqlTableModel.setFilter(f"{query_key} ='%s'" % (select_value))
        iFlg = self.SqlTableModel.select()
        if iFlg:
            print("过滤字段值成功")

    def sort_Data(self):
        print("进入Sort_data")
        if self.sort_comboBox.currentIndex() == 0:
            QMessageBox.information(self, "提示", "请选择排序字段！")
            self.ascend_radioButton.setCheckable(False)
            self.ascend_radioButton.setCheckable(True)
            self.descend_radioButton.setCheckable(False)
            self.descend_radioButton.setCheckable(True)
            return
        else:
            if self.SqlTableModel:
                sort_id = self.sort_comboBox.currentIndex()
                if self.ascend_radioButton.isChecked():
                    self.SqlTableModel.setSort(sort_id, Qt.AscendingOrder)
                elif self.descend_radioButton.isChecked():
                    self.SqlTableModel.setSort(sort_id, Qt.DescendingOrder)
                self.SqlTableModel.select()
            else:
                QMessageBox.information(self, "提示", "请先打开一个数据库！")
                return

    # 创建一个数据库
    def create_db(self, *args, **kwargs):
        try:
            # 调用输入框获取数据库名称
            if kwargs.get('db_name') == None:
                if self.root.proj_hems != None:
                    db_string, db_action = QInputDialog.getText(self, '打开或创建数据库', '请输入数据库和表的名称，以逗号分隔',
                                                                text='{},{}'.format(self.root.proj_hems.db_name,
                                                                                    self.root.proj_hems.db_table_name))
                else:
                    db_string, db_action = QInputDialog.getText(self, '打开或创建数据库', '请输入数据库和表的名称，以逗号分隔')
                List_temp = db_string.split(',')
                db_text = List_temp[0]
                if len(List_temp) == 2:
                    db_table = List_temp[1]
                else:
                    db_table = 'PO'
            else:
                db_text = kwargs['db_name']
                db_action = True
                if kwargs.get('table_name') != None:
                    db_table = kwargs['table_name']
                else:
                    db_table = 'PO'

            if (db_text.replace(' ', '') != '') and (db_action is True):
                # print(db_text)
                self.db_name = db_text
                self.db_table_name = db_table
                self.db_list = []

                # 添加一个sqlite数据库连接并打开, db是数据库的句柄类, 注意第二个参数要设置, 不同数据库用数据库名进行区别，为多数据库预留接口
                self.db_list.append(QSqlDatabase.addDatabase('QSQLITE', self.db_name))
                self.db_list[-1].setDatabaseName('{}.sqlite'.format(self.db_name))
                self.db_list[-1].open()
                print('数据库{}链接成功'.format(self.db_name + '.sqlite'))

                # 实例化一个数据库的操作对象
                query = QSqlQuery(self.db_list[-1])

                # 在数据库中创建一个表，并插入表头
                iFlag = query.exec_(
                    f"create table if not exists {self.db_table_name}(ID integer PRIMARY KEY AUTOINCREMENT, "
                    "freq varchar(20), elev varchar(20), azim varchar(20), "
                    "rcs_vv varchar(20), rcs_hh varchar(20))")

                if iFlag:
                    print('在数据库中成功创建数据表{}'.format(self.db_table_name))

                if self.iFlgsql_ui == 0:     #没有界面则直接返回
                    self.SqlTableModel = query
                    return

                self.SqlTableModel = QSqlTableModel(db=self.db_list[-1])  # 绑定数据库
                self.tableView.setModel(self.SqlTableModel)

                self.SqlTableModel.setTable(self.db_table_name)  # 设置数据模型的数据表
                self.SqlTableModel.setEditStrategy(QSqlTableModel.OnFieldChange)  # 允许字段更改

                iFlag = self.SqlTableModel.select()
                if iFlag:
                    print("表中内容选择成功！")
                while self.SqlTableModel.canFetchMore():
                    self.SqlTableModel.fetchMore()

                self.SqlTableModel.setHeaderData(0, Qt.Horizontal, 'ID')
                self.SqlTableModel.setHeaderData(1, Qt.Horizontal, 'Frequence')
                self.SqlTableModel.setHeaderData(2, Qt.Horizontal, 'Elevation')
                self.SqlTableModel.setHeaderData(3, Qt.Horizontal, 'Azimuth')
                self.SqlTableModel.setHeaderData(4, Qt.Horizontal, 'RCS_VV')
                self.SqlTableModel.setHeaderData(5, Qt.Horizontal, 'RCS_HH')
        except Exception as e:
            print(e)

    # 打开一个数据库
    def open_db(self, *args, **kwargs):
        try:
            # 调用输入框获取数据库名称
            if kwargs.get('db_name') == None:
                if self.root.proj_hems != None:
                    db_string, db_action = QInputDialog.getText(self, '打开或创建数据库', '请输入数据库和表的名称，以逗号分隔',
                                                                text='{},{}'.format(self.root.proj_hems.db_name,
                                                                                    self.root.proj_hems.db_table_name))
                else:
                    db_string, db_action = QInputDialog.getText(self, '打开或创建数据库', '请输入数据库和表的名称，以逗号分隔')
                List_temp = db_string.split(',')
                db_text = List_temp[0]
                if len(List_temp) == 2:
                    db_table = List_temp[1]
                else:
                    db_table = 'PO'
            else:
                db_text = kwargs['db_name']
                db_action = True
                if kwargs.get('table_name') != None:
                    db_table = kwargs['table_name']
                else:
                    db_table = 'PO'

            if (db_text.replace(' ', '') != '') and (db_action is True):
                # print(db_text)
                self.db_name = db_text
                self.db_table_name = db_table
                self.db_list = []

                # 添加一个sqlite数据库连接并打开, db是数据库的句柄类, 注意第二个参数要设置, 不同数据库用数据库名进行区别，为多数据库预留接口
                self.db_list.append(QSqlDatabase.addDatabase('QSQLITE', self.db_name))
                self.db_list[-1].setDatabaseName('{}.sqlite'.format(self.db_name))
                self.db_list[-1].open()
                print('数据库{}链接成功'.format(self.db_name + '.sqlite'))

                # 实例化一个数据库的操作对象
                query = QSqlQuery(self.db_list[-1])

                if self.iFlgsql_ui == 0:     #没有界面则直接返回
                    self.SqlTableModel = query
                    return

                self.SqlTableModel = QSqlTableModel(db=self.db_list[-1])  # 绑定数据库
                self.tableView.setModel(self.SqlTableModel)

                self.SqlTableModel.setTable(self.db_table_name)  # 设置数据模型的数据表
                self.SqlTableModel.setEditStrategy(QSqlTableModel.OnFieldChange)  # 允许字段更改

                iFlag = self.SqlTableModel.select()
                if iFlag:
                    print("表中内容选择成功！")
                while self.SqlTableModel.canFetchMore():
                    self.SqlTableModel.fetchMore()

                self.SqlTableModel.setHeaderData(0, Qt.Horizontal, 'ID')
                self.SqlTableModel.setHeaderData(1, Qt.Horizontal, 'Frequence')
                self.SqlTableModel.setHeaderData(2, Qt.Horizontal, 'Elevation')
                self.SqlTableModel.setHeaderData(3, Qt.Horizontal, 'Azimuth')
                self.SqlTableModel.setHeaderData(4, Qt.Horizontal, 'RCS_VV')
                self.SqlTableModel.setHeaderData(5, Qt.Horizontal, 'RCS_HH')
        except Exception as e:
            print(e)

    # ------------ 添加一行数据---------------
    def add_row_data(self):

        self.freq_lineEdit.setEnabled(True)
        self.freq_lineEdit.setValidator(self.regExpValidator)
        self.elev_lineEdit.setEnabled(True)
        self.elev_lineEdit.setValidator(self.doubleValidator)
        self.azim_lineEdit.setEnabled(True)
        self.azim_lineEdit.setValidator(self.doubleValidator)
        self.rcs_vv_lineEdit.setEnabled(True)
        self.rcs_vv_lineEdit.setValidator(self.doubleValidator)
        self.rcs_hh_lineEdit.setEnabled(True)
        self.rcs_hh_lineEdit.setValidator(self.doubleValidator)
        self.insert_pushButton.setEnabled(True)
        QMessageBox.information(self, "提示", "请输入要插入的数据，然后点击插入数据键")
        self.insert_pushButton.clicked.connect(self.insert_data)

    def insert_data(self, **kwargs):
        if self.SqlTableModel == None:
            print("请先打开或创建一个数据库")
            return
        if self.iFlgsql_ui == 1:
            freq_temp = float(self.freq_lineEdit.text())
            elev_temp = float(self.elev_lineEdit.text())
            azim_temp = float(self.azim_lineEdit.text())
            rcs_vv_temp = float(self.rcs_vv_lineEdit.text())
            rcs_hh_temp = float(self.rcs_hh_lineEdit.text())
        else:
            if (kwargs.get('freq') == None or kwargs.get('elev') == None or kwargs.get('azim') == None or \
                    kwargs.get('rcs_vv') == None or kwargs.get('rcs_hh') == None):
                QMessageBox.Warning(self, "警告", "仿真结果缺少参数（frequence or elevation or azimth or rcs_vv or rcs_hh），请检查!")
                return
            else:
                freq_temp = kwargs['freq']
                elev_temp = kwargs['elev']
                azim_temp = kwargs['azim']
                rcs_vv_temp = kwargs['rcs_vv']
                rcs_hh_temp = kwargs['rcs_hh']

        #--------- 向数据库中插入数据--------
        query = QSqlQuery(self.db_list[-1])
        iFlag = query.exec_(f"insert into {self.db_table_name}(freq, elev, azim, rcs_vv, rcs_hh) "
                            f"values({freq_temp}, {elev_temp}, {azim_temp}, "
                            f"{rcs_vv_temp}, {rcs_hh_temp} )")
        if iFlag:
            print("向数据库中插入数据成功")
        else:
            QMessageBox.information(self, "提示", "插入数据失败！")

        if self.iFlgsql_ui:
            self.SqlTableModel.select()
            self.freq_lineEdit.setEnabled(False)
            self.elev_lineEdit.setEnabled(False)
            self.azim_lineEdit.setEnabled(False)
            self.rcs_vv_lineEdit.setEnabled(False)
            self.rcs_hh_lineEdit.setEnabled(False)
            self.insert_pushButton.setEnabled(False)
            self.insert_pushButton.clicked.disconnect(self.insert_data)

    # ------------删除一行数据-----------------
    def del_row_data(self):
        if self.SqlTableModel:
            self.SqlTableModel.removeRow(self.tableView.currentIndex().row())

    # ------------关闭数据库---------------
    def close_db(self):
        for db in self.db_list:  # 关闭所有的数据库链接
            db.close()
        self.SqlTableModel = None
        if self.iFlgsql_ui:
            self.SqlTableModel.clear()  # 清除表格 并 reset

if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = Form()
    form.show()
    sys.exit(app.exec_())
